﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Interop;
using WindowsInput;
using System.IO;

namespace MercuryFE
{

    /// <summary>
    /// Takes care of launching and closing games...
    /// </summary>
    public class ProcessHandler
    {

        public delegate void ProcessCompletedHandler(string ErrorMessage);
        public event ProcessCompletedHandler ProcessComplete;

        private Process _process = null;
        private MameInterop _mameInterop = null;
        //private bool _processMax = true;



        private DateTime _startTime;
        private int _gameId = -1;

        private string _processDetails = "";
        private string _errorMessage = "";
        private MyWindow _parentWindow = null;

        public string ErrorMessage { get { return _errorMessage; } }

        public IntPtr ProcessWindowHandle { get { return _process.MainWindowHandle; } }


        #region overlay

        enum ShowWindowCommands : int
        {
            /// <summary>
            /// Hides the window and activates another window.
            /// </summary>
            Hide = 0,
            /// <summary>
            /// Activates and displays a window. If the window is minimized or
            /// maximized, the system restores it to its original size and position.
            /// An application should specify this flag when displaying the window
            /// for the first time.
            /// </summary>
            Normal = 1,
            /// <summary>
            /// Activates the window and displays it as a minimized window.
            /// </summary>
            ShowMinimized = 2,
            /// <summary>
            /// Maximizes the specified window.
            /// </summary>
            Maximize = 3, // is this the right value?
            /// <summary>
            /// Activates the window and displays it as a maximized window.
            /// </summary>      
            ShowMaximized = 3,
            /// <summary>
            /// Displays a window in its most recent size and position. This value
            /// is similar to <see cref="Win32.ShowWindowCommand.Normal"/>, except
            /// the window is not activated.
            /// </summary>
            ShowNoActivate = 4,
            /// <summary>
            /// Activates the window and displays it in its current size and position.
            /// </summary>
            Show = 5,
            /// <summary>
            /// Minimizes the specified window and activates the next top-level
            /// window in the Z order.
            /// </summary>
            Minimize = 6,
            /// <summary>
            /// Displays the window as a minimized window. This value is similar to
            /// <see cref="Win32.ShowWindowCommand.ShowMinimized"/>, except the
            /// window is not activated.
            /// </summary>
            ShowMinNoActive = 7,
            /// <summary>
            /// Displays the window in its current size and position. This value is
            /// similar to <see cref="Win32.ShowWindowCommand.Show"/>, except the
            /// window is not activated.
            /// </summary>
            ShowNA = 8,
            /// <summary>
            /// Activates and displays the window. If the window is minimized or
            /// maximized, the system restores it to its original size and position.
            /// An application should specify this flag when restoring a minimized window.
            /// </summary>
            Restore = 9,
            /// <summary>
            /// Sets the show state based on the SW_* value specified in the
            /// STARTUPINFO structure passed to the CreateProcess function by the
            /// program that started the application.
            /// </summary>
            ShowDefault = 10,
            /// <summary>
            ///  <b>Windows 2000/XP:</b> Minimizes a window, even if the thread
            /// that owns the window is not responding. This flag should only be
            /// used when minimizing windows from a different thread.
            /// </summary>
            ForceMinimize = 11
        }




        //http://www.daniweb.com/forums/thread288007.html
        [DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
        public static extern IntPtr GetDesktopWindow();

        [DllImport("user32.dll", CharSet = CharSet.Auto)]
        public static extern IntPtr FindWindow(
            [MarshalAs(UnmanagedType.LPTStr)] string lpClassName,
            [MarshalAs(UnmanagedType.LPTStr)] string lpWindowName);

        [DllImport("user32.dll")]
        public static extern IntPtr SetParent(
             IntPtr hWndChild,      // handle to window
             IntPtr hWndNewParent   // new parent window
           );

        [DllImport("user32.dll", SetLastError = true)]
        static extern int GetWindowLong(IntPtr hWnd, int nIndex);

        [DllImport("user32.dll", SetLastError = true)]
        static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong);

        [DllImport("user32.dll")]
        static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);

        // Activate an application window.
        [DllImport("USER32.DLL")]
        public static extern bool SetForegroundWindow(IntPtr hWnd);


        private const int GWL_EXSTYLE = -20;
        private const int WS_EX_TRANSPARENT = 0x20;



        //private void showOverlay(Window win)
        //{
        //    IntPtr handle = new WindowInteropHelper(win).Handle; //, Process.GetCurrentProcess().MainWindowHandle);

        //    //var hwnd = ((HwndSource)PresentationSource.FromVisual(handle)).Handle;

        //    int exstyle = GetWindowLong(handle, GWL_EXSTYLE);
        //    exstyle |= WS_EX_TRANSPARENT;
        //    SetWindowLong(handle, GWL_EXSTYLE, exstyle);

        //    IntPtr hwndf = handle;
        //    IntPtr hwndParent = GetDesktopWindow();
        //    SetParent(hwndf, hwndParent);

        //    win.Topmost = true;
        //}

        #endregion

        public ProcessHandler(MyWindow ParentWindow)
        {
            _parentWindow = ParentWindow;
        }

        public enum ShowWindowState : int
        {
            SW_HIDE = 0,
            SW_MAXIMIZE = 3,
            SW_MINIMIZE = 6,
            SW_RESTORE = 9,
            SW_SHOW = 5,
            SW_SHOWDEFAULT = 10,
            SW_SHOWMAXIMIZED = 3,
            SW_SHOWMINIMIZED = 2,
            SW_SHOWMINNOACTIVE = 7,
            SW_SHOWNA = 8,
            SW_SHOWNOACTIVATE = 4,
            SW_SHOWNORMAL = 1
        };

        //private void HookManager_KeyDown(object sender, System.Windows.Forms.KeyEventArgs e)
        //{
        //    // if they press 'p', toggle minimize, maximize
        //    if (e.KeyCode == System.Windows.Forms.Keys.P)
        //    {




        //        // now minimize and maximize
        //        if (_processMax)
        //        {
        //            // send P before minimising
        //            InputSimulator.SimulateKeyPress((VirtualKeyCode)Enum.Parse(typeof(VirtualKeyCode), "VK_P"));
        //            ShowWindow(_process.MainWindowHandle, (int)ShowWindowState.SW_SHOWMINIMIZED);
        //            ShowWindow(Process.GetCurrentProcess().MainWindowHandle, (int)ShowWindowState.SW_SHOWMAXIMIZED);

        //        }
        //        else
        //        {
        //            ShowWindow(_process.MainWindowHandle, (int)ShowWindowState.SW_SHOWMAXIMIZED);
        //            ShowWindow(Process.GetCurrentProcess().MainWindowHandle, (int)ShowWindowState.SW_SHOWMINIMIZED);

        //            // send p after maximising
        //            InputSimulator.SimulateKeyPress((VirtualKeyCode)Enum.Parse(typeof(VirtualKeyCode), "VK_P"));

        //        }

        //        _processMax = !_processMax;
        //    }
        //}

        //private void _process_Exited(object sender, EventArgs e)
        //{
        //    //HookManager.KeyDown -= HookManager_KeyDown;

        //    // calculate played time...
        //    TimeSpan playTime = DateTime.Now.Subtract(_startTime);

        //    ClientLib.saveGamePlayedStats(_gameId, (int)playTime.TotalMinutes);
        //    ShowWindow(_process.MainWindowHandle, (int)ShowWindowState.SW_SHOWMAXIMIZED);

        //    // MessageBox.Show("complete");

        //}



        public bool LaunchGame(int gameId, ref string error, ref string commandline)
        {
            // make sure before we start, that everything else has been cleaned up from last time
            if (_mameInterop != null)
            {
                _mameInterop.Dispose();
                _mameInterop = null;
            }

            if (_process != null)
            {
                _process.Kill();
                _process.Close();
                _process.Dispose();
                _process = null;
            }

            // declarations
            _gameId = gameId;
            string executable = "";
            string arguments = "";
            bool isMame = false;

            ClientLib.getGameExecutionInfo(gameId, ref executable, ref arguments, ref isMame);


            _processDetails = "Executable: " + executable + Environment.NewLine + "Arguments: " + arguments;


            commandline = executable + " " + arguments;

            ProcessStartInfo theProcess = new ProcessStartInfo(executable);
            theProcess.WindowStyle = ProcessWindowStyle.Normal;
            theProcess.CreateNoWindow = true;
            theProcess.Arguments = arguments;
            theProcess.WorkingDirectory = Path.GetDirectoryName(executable); 
            theProcess.UseShellExecute = false;
            theProcess.RedirectStandardError = true;

            _startTime = DateTime.Now;
            try
            {
                if (isMame)
                {
                    // launch mame interop process (only if the game itself is mame)
                    _mameInterop = new MameInterop(99,"mame interop");

                    //_mameInterop.Initialize(2, );
                    _mameInterop.MamePaused += new MameInterop.MamePausedHandler(MameInterop_MamePaused);
                }

                // launch the actuall mame proces
                _process = Process.Start(theProcess);

                _process.EnableRaisingEvents = true;
                _process.Exited += new EventHandler(_process_Exited);

                if(App.__soundHandler != null)
                    App.__soundHandler.pause();

                SetForegroundWindow(_process.MainWindowHandle);
            }
            catch (Exception e)
            {
                // set back to standard, since process isn't running 
                App.__navigationHook.CurrentNavigationType = NavigationHook.NavigationType.Standard;
                _errorMessage = 
                      "Error launching process!" + Environment.NewLine
                    + "Message: " + e.Message + Environment.NewLine
                    + "Process Details: " + Environment.NewLine 
                    + _processDetails;

                if (_mameInterop != null)
                {
                    _mameInterop.Dispose();
                    _mameInterop = null;
                }   

                return false;
            }


            App.__navigationHook.ProcessWindowHandle = _process.MainWindowHandle;
            return true;

        }

        void MameInterop_MamePaused(bool IsPaused)
        {
            // first time game is paused - we launch game details page....
            _parentWindow.IsMamePaused = IsPaused;
            if (IsPaused)
            {
                //_parentWindow.
                //IntPtr handle = (new System.Windows.Interop.WindowInteropHelper(_parentWindow)).Handle;
                //SetForegroundWindow(_parentWindow.getHandle()); //Process.GetCurrentProcess().MainWindowHandle);
                ProcessWindow mfeWindow = new ProcessWindow("MercuryFE", _parentWindow.getHandle(), "");
                mfeWindow.Activate();

                //_parentWindow.Activate();
                _parentWindow.PauseMame();
            }

            // next time, when we unpause game - nothing is done - side affect is 'double' press of pause

        }

        public void MameUnpaused()
        {
            ProcessWindow gameWindow = new ProcessWindow("Game", _process.MainWindowHandle, "");
            gameWindow.Activate();

            //SetForegroundWindow(_process.MainWindowHandle);
            //ShowWindow(_process.MainWindowHandle, (int)ShowWindowCommands.Show);
        }





        void _process_Exited(object sender, EventArgs e)
        {
            //SetForegroundWindow(Process.GetCurrentProcess().MainWindowHandle);
            if (_mameInterop != null)
            {
                try
                {
                    _mameInterop.Dispose();
                }
                catch (Exception)
                {
                    // wonder why this causes an exception on disposal??
                }
                _mameInterop = null;
            }

            if (App.__soundHandler != null)
                App.__soundHandler.resume();

            string error = _process.StandardError.ReadToEnd();
           
            TimeSpan playTime = DateTime.Now.Subtract(_startTime);

            // make some crazy assumptions :D

             App.__navigationHook.CurrentNavigationType = NavigationHook.NavigationType.Standard;
            App.__navigationHook.ProcessWindowHandle = IntPtr.Zero;



            if (error == "" || playTime.Seconds > 20) // if no error or if played for more than 20 seconds, than not an error - probably some mame warning 
            {
                ClientLib.saveGamePlayedStats(_gameId, (int)playTime.TotalMinutes);
                _errorMessage = "";
            }
            else
            {
                _errorMessage = "Error running process!" + Environment.NewLine
                    + "Message: " + error + Environment.NewLine
                    + _processDetails;
                
            }
            ProcessComplete(_errorMessage);

            if (_process != null)
            {
                _process.Close();
                _process.Dispose();
                _process = null;
            }

        }

    }

}
